
package com.jf.cloud.product.task;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.jf.cloud.common.cache.constant.CacheNames;
import com.jf.cloud.common.cache.constant.LockCacheNames;
import com.jf.cloud.common.cache.constant.ProductCacheNames;
import com.jf.cloud.common.cache.util.RedisUtil;
import com.jf.cloud.common.product.vo.SpuVO;
import com.jf.cloud.product.bo.NeedZoneSkuInfoBO;
import com.jf.cloud.product.constant.SkuZoneConstant;
import com.jf.cloud.product.mapper.SkuStockLockMapper;
import com.jf.cloud.product.mapper.SkuStockMapper;
import com.jf.cloud.product.mapper.SkuStockZoneMapper;
import com.jf.cloud.product.mapper.SpuMapper;
import com.jf.cloud.product.model.Giveaway;
import com.jf.cloud.product.model.SkuStock;
import com.jf.cloud.product.service.GiveawayService;
import com.jf.cloud.product.service.SkuStockService;
import com.jf.cloud.product.service.SkuStockZoneService;
import com.jf.cloud.product.service.SpuService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.apache.commons.collections4.CollectionUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 商品定时任务
 *
 * @author zz
 */
@Component
public class ProductTask {
    private static final Logger log = LoggerFactory.getLogger(ProductTask.class);

    @Autowired
    private SpuService spuService;
    @Autowired
    private SkuStockLockMapper skuStockLockMapper;
    @Autowired
    private SpuMapper spuMapper;
    @Autowired
    private SkuStockZoneService skuStockZoneService;
    @Autowired
    private GiveawayService giveawayService;
    @Autowired
    private SkuStockZoneMapper skuStockZoneMapper;
    @Autowired
    private SkuStockMapper skuStockMapper;


    /**
     * 校验商品数量是否完整
     * 商品数据是否为最新的：根据商品更新时间判断
     */
    @XxlJob("verifySpuData")
    public void verifySpuData(){
        spuService.verifySpuData();
    }

    /**
     * 结束赠品活动
     */
    @XxlJob("activityFinishAndProdChange")
    public void activityFinishAndProdChange() {
        XxlJobHelper.log("赠品活动结束，主商品恢复为普通商品。。。");
        // -1：已删除 0：关闭 1:开启 2：未开启
        // 获取活动结束，需要改变商品类型的列表
        List<Giveaway> giveawayActivityList = giveawayService.listUnEndButNeedEndActivity();

        if (CollUtil.isEmpty(giveawayActivityList)){
            return;
        }
        giveawayService.changeSpuTypeByGiveawayActivityIdList(giveawayActivityList);
    }

    /**
     * 启用赠品活动
     */
    @XxlJob("activityStartAndProdChange")
    public void activityStartAndProdChange(){
        XxlJobHelper.log("赠品活动开启");
        giveawayService.startGiveawayActivity();
    }


    @XxlJob("offlineExpireVirtualProd")
    public void offlineExpireVirtualProd(){
        XxlJobHelper.log("过了核销时间的虚拟商品，进行下架操作。");
        // 获取30分钟之前未支付的订单
        List<SpuVO> spuVOList = spuService.handleExpireVirtualProd();
        if(CollectionUtils.isEmpty(spuVOList)){
            return;
        }
        List<Long> ids = new ArrayList<>();
        for (SpuVO spuVO : spuVOList) {
            //清除缓存
            spuService.removeSpuCacheBySpuId(spuVO.getSpuId());
            ids.add(spuVO.getSpuId());
        }
    }



    /**
     * 对sku库存进行分片
     */
    @XxlJob("zoneSkuIfNecessary")
    public void zoneSkuIfNecessary(){
        List<NeedZoneSkuInfoBO> needZoneSkus = skuStockLockMapper.listNeedZoneSkuInfo(SkuZoneConstant.NEED_ZONE_NUM);
        List<Long> spuIds = needZoneSkus.stream().map(NeedZoneSkuInfoBO::getSpuId).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(spuIds)) {
            return;
        }
        log.info("对sku库存进行分片。。。{}", needZoneSkus);
        // 假如有供应商商品，还要获取代销商品的spuId
        List<Long> supplierSpuIds = spuMapper.getSupplierSpuIds(spuIds);
        if (CollUtil.isNotEmpty(supplierSpuIds)) {
            spuIds.addAll(supplierSpuIds);
        }
        skuStockZoneService.zoneSkuIfNecessary(spuIds, needZoneSkus);
        // 清除商品分片缓存
        List<String> keys = new ArrayList<>();
        for (Long spuId : spuIds) {
            keys.add(ProductCacheNames.SPU_ZONE + CacheNames.UNION + spuId);
        }
        for (NeedZoneSkuInfoBO zoneSkus : needZoneSkus) {
            Long skuId = zoneSkus.getSkuId();
            keys.add(ProductCacheNames.SKU_ZONE_INFO + CacheNames.UNION + skuId);
        }
        RedisUtil.del(keys);
        log.info("对sku库存进行分片结束。。。");
    }

    /**
     * 重新分片，如果有必要的话
     * 因为取消订单/退款/进销存/直接修改库存（也就是说除了下单以外的所有库存操作）都在主分片操作
     * 这个时候会导致主库存分片的数量越来越多，而其他分片的库存会和主分片的库存越来越不均匀，此时就要重新分过片
     */
    @XxlJob("reZoneSkuIfNecessary")
    public void reZoneSkuIfNecessary(){
        // 情况一：
        // 是主分片的库存过多，但是分片数量不知道干嘛用完了（并发太高了，还没有进行重新分片的时候，分片就用完了），要重新分片
        List<Long> skuIds = skuStockMapper.listNeedReZoneSku(SkuZoneConstant.NEED_ZONE_NUM);
        skuStockZoneService.reZoneByCollection(skuIds);

        // 情况二：
        // 我们先确定一件事情，就是库存至少要有200才进行分片，如果低于200，那么全部都会在一个还未分片的主分片
        // 根据skuStockZoneService.doZone 这里面的分片算法，如果需要进行分片，
        // 比如201时会有分片此时会分成 主分片100 分片1 51 分片2 50，也就是说其实一个分片的最小库存数量应该是50，如果低于50，那么就已经意味着有必要要进行重新分片了
        // 是不是意味着低于50就立即进行重新分片呢？那也未必，毕竟为了并发考虑50其实也还是能够扣减，而且如果刚分配完又重新收回岂不是很尴尬
        // 30个就比较好了，如果有个次分片低于30(但是要大于0，全部为0的话会走上面的sql)的话呢，就进行重新分片了
        Set<Long> skuIdSet = skuStockZoneMapper.listNeedReZoneSku(SkuZoneConstant.NEED_REZONE_NUM);
        skuStockZoneService.reZoneByCollection(skuIdSet);

        // 情况三：
        // 分片只是一个临时状态，那么当一个sku从热销状态到后来销量下降，不再有并发问题时，就要把它的分片状态切换到未分片状态
        // 那么判断这个销量下降也可以参考sku需要分片的sql，也就是说当五秒钟内该sku的销量小于10的时候，就需要切换回去了
        List<Long> unZoneSkuIds = skuStockLockMapper.listUnZoneSkuInfo(SkuZoneConstant.UN_ZONE_NUM);
        skuStockZoneService.unZoneByCollection(unZoneSkuIds);
    }
}
